import collections
import os.path
from universal.common_header import *
from universal.common_header_widget import *
from universal.custom.ini_file import ODvision_config,ODModule_config
from universal.widget_common import createLabel,SetCellWidget,lineEdit_act
from Module.MainWindow.XMLaug import StartIMG
from xml.dom.minidom import Document
import os
import json
import numpy as np

class Tablewidget(QTableWidget):
    def __init__(self,  row, column,parent=None):
        super(Tablewidget, self).__init__(parent)

        self.setColumnCount(column)
        self.setRowCount(row)
        self.horizontalHeader().setStretchLastSection(True)
        self.verticalHeader().setVisible(False)
        self.horizontalHeader().setVisible(False)

    def Tablewidget_setItem(self,name_list): #设置第一列的名字且不可编辑
        name_List_back = []
        for i in name_list:
            name = QTableWidgetItem(i)
            name_List_back.append(name)
        for i in range(len(name_List_back)):
            self.setItem(i, 0, name_List_back[i])
            name_List_back[i].setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)


    def SetCellWidget(self, list_):    #嵌入控件
        for idx, i in enumerate(list_):
            self.setCellWidget(idx, 1, i)


    def insertTablewidget(self,row,widget):
        self.insertRow(row)
        self.setSpan(row, 0, 1, 2)  # 合并的行数；列数；需要合并几行；几列
        self.setCellWidget(row, 0, widget)

    def setAct(self, picture, NoEdit=False):  # 为你的编辑框设置小图标按钮
        imgspathLineEdit = QLineEdit()
        if NoEdit:
            imgspathLineEdit.setReadOnly(True)
        actLine = QAction(imgspathLineEdit)
        actLine.setIcon(QIcon(picture))
        imgspathLineEdit.addAction(actLine, QLineEdit.TrailingPosition)
        return imgspathLineEdit, actLine

#-------------------------------------------------
class Mainwindow_Label_table(QWidget):
    def __init__(self, row, column,namelist,parent=None):
        super().__init__(parent=parent)
        self.mainwindow = parent
        self.TabelParameter_Input = Tablewidget(row, column)
        self.TabelParameter_Input.Tablewidget_setItem(namelist)

    # ------------------------第一个Tab-------------------------------------------
    def widget(self): #标注的Tabel
        self.imgspathLineEdit, actLineImg = setAct(self, ':/icons/1-1.ico')
        actLineImg.triggered.connect(lambda: self.mainwindow.getSavePath(self.imgspathLineEdit, mark=True))
        self.HalfAuto, act = setAct(self.mainwindow, ':/icons/1-2.ico', True)
        act.triggered.connect(lambda: self.LoadHalfAuto())
        self.CurrentLabel = QComboBox()
        self.CurrentLabel.currentIndexChanged[int].connect(self.currentIndexChanged)

        self.SetAllImg = Button('合并图像', lambda: self.merge_img_thread())
        self.TabelParameter_Input.SetCellWidget([self.imgspathLineEdit, self.HalfAuto, self.CurrentLabel])
        self.TabelParameter_Input.insertTablewidget(3, self.SetAllImg)
        return self.imgspathLineEdit, self.CurrentLabel,self.TabelParameter_Input,self.HalfAuto

    def LoadHalfAuto(self):
        if ODModule_config.read_value('TrainAndTest', 'page') == "1":
            self.HalfAuto.setText(os.path.join(self.mainwindow.testWidget.SaveNGImages,'NG_Original'))
            self.mainwindow.getSavePath(self.HalfAuto)

    def currentIndexChanged(self):
        self.mainwindow.fileListWidget.clear()
        self.mainwindow.mImgList = self.mainwindow.nameDict[self.CurrentLabel.currentText()]
        self.mainwindow.saveFile_()
        self.mainwindow.Class_change(self.mainwindow.labelImgsPath,self.mainwindow.mImgList)

    def merge_img_thread(self):
        self.mainwindow.TextBrowser.clear()
        if self.HalfAuto.text() == '':
            return
        self.saveFile_thread = threading.Thread(target=self.merge_img,args= '')
        self.saveFile_thread.daemon = True
        self.saveFile_thread.start()

    def merge_img(self):
        merge_list = scanAllImages(self.HalfAuto.text())
        if self.imgspathLineEdit.text().replace('/','\\') != self.HalfAuto.text().replace('/','\\'):
            for i in merge_list:
                shutil.copyfile(i, os.path.join(self.imgspathLineEdit.text(),os.path.basename(i)))
                xml = os.path.splitext(i)[0] + '.xml'
                shutil.copy(xml, self.imgspathLineEdit.text())
                self.mainwindow.TextBrowser.append(i)

    #---------------------------第三个Tab数据增强-----------------------------------------------------
    def widget2(self):  # 数据增强的Tabel
        self.InitImgPath = lineEdit_act(self)
        self.SaveImgPath = lineEdit_act(self)
        self.EnhanceTimes = QLineEdit(str(5))
        self.Fliplr = QLineEdit(str(0.5))
        self.Multiply = QLineEdit(str((0.9, 1.1)))
        self.GaussianBlur = QLineEdit(str((0, 1.0)))
        self.translate_X_px = QLineEdit(str(15))
        self.translate_Y_px = QLineEdit(str(15))
        self.scale = QLineEdit(str((0.9, 1.1)))  # 图像缩放为80%到95%之间
        self.rotate = QLineEdit(str((-5, 5)))  # 旋转±30度之间

        TabelParameter_Input_list = [self.InitImgPath, self.SaveImgPath, self.EnhanceTimes, self.Fliplr,
                                     self.Multiply, self.GaussianBlur, self.translate_X_px, self.translate_Y_px,
                                     self.scale, self.rotate]

        self.TabelParameter_Input.SetCellWidget(TabelParameter_Input_list)
        self.ButtonIni=1
        self.main_queue = queue.Queue()
        self.StartImgEnhance = Button('开始数据增强')
        self.StartImgEnhance.clicked.connect(self.StartEnhance)
        self.TabelParameter_Input.insertTablewidget(10, self.StartImgEnhance)
        return self.TabelParameter_Input

    def Tabchanged_path(self):
        if self.InitImgPath.text() == '':
            self.InitImgPath.setText(self.mainwindow.labelImgsPath)
            self.SaveImgPath.setText(self.mainwindow.labelImgsPath)

    def StartEnhance(self):
        if self.ButtonIni == 0:
            ButtonColor(self.StartImgEnhance,'开始数据增强')
            self.main_queue.put({'stop': True})
            self.ButtonIni = 1
        else:
            self.ButtonIni = 0
            self.main_queue.queue.clear()
            ButtonColor_stop(self.StartImgEnhance,'停止数据增强')
            cfg = {'InitImgPath': self.InitImgPath.text(),
                   'SaveImgPath': self.SaveImgPath.text(),
                   'EnhanceTimes': self.EnhanceTimes.text(),
                   'Fliplr': self.Fliplr.text(),
                   'Multiply': self.Multiply.text(),
                   'GaussianBlur': self.GaussianBlur.text(),
                   'translate_X': self.translate_X_px.text(),
                   'translate_Y': self.translate_Y_px.text(),
                   'scale': self.scale.text(),
                   'rotate': self.rotate.text(),
                   "main_queue" : self.main_queue
                   }
            StartEnhance = threading.Thread(target=StartIMG, args=(cfg,))
            StartEnhance.daemon = True
            StartEnhance.start()

            GetValThread = threading.Thread(target=self.GetVal, args=(self.main_queue,))
            GetValThread.daemon = True
            GetValThread.start()

    def GetVal(self,main):
        while True:
            time.sleep(0.1)
            if main != None:
                try:
                    flags = main.get(block=False)
                except:
                    flags = {}
                if flags.get('stop', False):
                    self.ButtonIni = 1
                    ButtonColor(self.StartImgEnhance, '开始数据增强')
                    main.put({'stop': True})
                    break

# ------------------------第四个Tab-------------------------------------------
    def widget3(self): #格式转换的Tabel
        self.Ini_widget3 = 1
        self.main_queueWidget3 = queue.Queue()

        self.InitImgPath_widget3 = lineEdit_act(self)
        self.SaveImgPath_widget3 = lineEdit_act(self)
        self.format_combox = QComboBox() #1
        self.format_combox.addItems(["xml->txt","txt->xml","json->txt","txt->json"]) #1
        self.input_Labelname = QLineEdit()
        self.TabelParameter_Input.SetCellWidget([self.InitImgPath_widget3, self.SaveImgPath_widget3
                                                    , self.format_combox, self.input_Labelname])
        self.Start_transFormat = Button('开始转换', lambda: self.TransFormat_thread())
        self.TabelParameter_Input.insertTablewidget(4, self.Start_transFormat)

        return self.TabelParameter_Input

    def TransFormat_thread(self):
        if self.Ini_widget3 == 0:
            ButtonColor(self.Start_transFormat, '开始转换')
            self.main_queueWidget3.put({'stop': True})
            self.Ini_widget3 = 1
        else:
            self.Ini_widget3 = 0
            self.main_queueWidget3.queue.clear()
            ButtonColor_stop(self.Start_transFormat, '停止转换')
            cfg = {'InitImgPath': self.InitImgPath_widget3.text(),
                   'SaveImgPath': self.SaveImgPath_widget3.text(),
                   'Labels': self.input_Labelname.text(),
                   "main_queue": self.main_queueWidget3
                   }
            if self.format_combox.currentText() == 'xml->txt':
                StartThread = threading.Thread(target=self.xml2txt_pre, args=(cfg["InitImgPath"]
                                             ,cfg["SaveImgPath"],cfg["Labels"],cfg["main_queue"]))
            elif self.format_combox.currentText() == 'txt->xml':
                StartThread = threading.Thread(target=self.txt2xml_pre, args=(cfg["InitImgPath"]
                                             ,cfg["SaveImgPath"], cfg["Labels"],cfg["main_queue"]))
            elif self.format_combox.currentText() == 'json->txt':
                StartThread = threading.Thread(target=self.json2txt_pre, args=(cfg["InitImgPath"]
                                            , cfg["SaveImgPath"], cfg["Labels"], cfg["main_queue"]))
            elif self.format_combox.currentText() == 'txt->json':
                StartThread = threading.Thread(target=self.xml2txt_pre, args=(cfg["InitImgPath"]
                                            ,cfg["SaveImgPath"], cfg["Labels"], cfg["main_queue"]))
            else:
                return
            StartThread.daemon = True
            StartThread.start()
            getVal_info_thread = threading.Thread(target=self.getVal_info, args=(self.main_queueWidget3,))
            getVal_info_thread.daemon = True
            getVal_info_thread.start()

    def getVal_info(self, main):
        while True:
            time.sleep(0.1)
            if main != None:
                try:
                    flags = main.get(block=False)
                except:
                    flags = {}
                if flags.get('stop', False):
                    self.Ini_widget3 = 1
                    ButtonColor(self.Start_transFormat, '开始转换')
                    main.put({'stop': True})
                    break

    def xml2txt_pre(self,xml_path: str, label_path: str, names,mainqueue):
        xmllist = get_img_list(xml_path, [], ext=['xml'])
        MK_dir(label_path)
        for xmlpath in xmllist:
            try:
                if mainqueue.get(block=False):
                    mainqueue.put({'stop': True})
                    break
            except Exception as e:
                pass
            xmlname = os.path.basename(xmlpath)
            qianzhui = os.path.splitext(xmlname)[0]
            txtpath = label_path + os.sep + qianzhui + '.txt'
            names = self.xml2txt(xmlpath,txtpath,names,mainqueue)
        mainqueue.put({'stop': True})

    def xml2txt(self,xml_path: str, label_path: str, names,mainqueue):
        '''
        <annotation>
            <filename>1.jpg</filename>
            <size>
                <width></width>
                <height></height>
                <depth></depth>
            </size>
            <segmented>0</segmented>
            <object>
                <name></name>
                <bndbox>
                    <xmin></xmin>
                    <ymin></ymin>
                    <xmax></xmax>
                    <ymax></ymax>
                </bndbox>
            </object>
        </annotation>
        '''

        from xml.dom.minidom import Element, Node, CDATASection, parse
        tree: Element = parse(xml_path)

        def get_data(tree: Element, name):
            node: Node = tree.getElementsByTagName(name)[0]
            section: CDATASection = node.childNodes[0]
            return section.data

        try:
            width = int(get_data(tree, 'width'))
            height = int(get_data(tree, 'height'))
        except:
            print('Could not parse size in ', xml_path)
            return

        txt_content = ''
        names = collections.OrderedDict(names)
        for obj in tree.getElementsByTagName('object'):
            name = get_data(obj, 'name')
            names[name] = None
            id_ = list(names).index(name)
            xmin = int(get_data(obj, 'xmin'))
            ymin = int(get_data(obj, 'ymin'))
            xmax = int(get_data(obj, 'xmax'))
            ymax = int(get_data(obj, 'ymax'))
            xc = (xmax + xmin) / 2 / width
            yc = (ymax + ymin) / 2 / height
            w = (xmax - xmin) / width
            h = (ymax - ymin) / height
            if not (str(xc) in txt_content and str(yc) in txt_content and
                    str(w) in txt_content and str(h) in txt_content):
                txt_content += f'{id_} {xc} {yc} {w} {h}\n'

        for obj in tree.getElementsByTagName('objects'):
            name = get_data(obj, 'name')
            names[name] = None
            id_ = list(names).index(name)
            txt_content += str(id_)
            points = str(get_data(obj, 'points')).split(';')

            for point in points:
                if not point: continue
                txt_content += ' ' + str(float(str(point).split(',')[0]) / width)
                txt_content += ' ' + str(float(str(point).split(',')[1]) / height)

            txt_content += '\n'

        open(label_path, 'w').write(txt_content)

        return names

    def txt2xml_pre(self,src_path: str, dst_path: str, names,mainqueue):
        MK_dir(dst_path)
        self.txt2xml(src_path,src_path,dst_path,names,mainqueue)
    def txt2xml(self,picPath, txtPath, xmlPath,names,mainqueue):
        """此函数用于将yolo格式txt标注文件转换为voc格式xml标注文件
        在自己的标注图片文件夹下建三个子文件夹，分别命名为picture、txt、xml
        """
        list_class = names.split(",") if len(names)>0 else []
        image_path = get_img_list(picPath,[], ext=['jpg', 'png', 'JPG', 'jpeg','bmp', 'PNG', 'JPEG', 'BMP'])
        for i, name in enumerate(image_path):
            try:
                if mainqueue.get(block=False):
                    mainqueue.put({'stop': True})
                    break
            except Exception as e:
                pass
            xmlBuilder = Document()
            annotation = xmlBuilder.createElement("annotation")  # 创建annotation标签
            xmlBuilder.appendChild(annotation)
            img = cv2.imdecode(np.fromfile(name, dtype=np.uint8),cv2.IMREAD_COLOR)
            Pheight, Pwidth, Pdepth = img.shape
            qianzhui = os.path.splitext(name)[0]
            if not os.path.exists(qianzhui + '.txt'):
                continue
            with open(qianzhui + '.txt', encoding='utf8') as txtFile:
                txtList = txtFile.readlines()
            folder = xmlBuilder.createElement("folder")  # folder标签
            foldercontent = xmlBuilder.createTextNode("driving_annotation_dataset")
            folder.appendChild(foldercontent)
            annotation.appendChild(folder)  # folder标签结束

            filename = xmlBuilder.createElement("filename")  # filename标签
            filenamecontent = xmlBuilder.createTextNode(name)
            filename.appendChild(filenamecontent)
            annotation.appendChild(filename)  # filename标签结束

            size = xmlBuilder.createElement("size")  # size标签
            width = xmlBuilder.createElement("width")  # size子标签width
            widthcontent = xmlBuilder.createTextNode(str(Pwidth))
            width.appendChild(widthcontent)
            size.appendChild(width)  # size子标签width结束

            height = xmlBuilder.createElement("height")  # size子标签height
            heightcontent = xmlBuilder.createTextNode(str(Pheight))
            height.appendChild(heightcontent)
            size.appendChild(height)  # size子标签height结束

            depth = xmlBuilder.createElement("depth")  # size子标签depth
            depthcontent = xmlBuilder.createTextNode(str(Pdepth))
            depth.appendChild(depthcontent)
            size.appendChild(depth)  # size子标签depth结束
            annotation.appendChild(size)  # size标签结束

            for j in txtList:
                oneline = j.strip().split(" ")
                object = xmlBuilder.createElement("object")  # object 标签
                picname = xmlBuilder.createElement("name") # name标签
                index = int(oneline[0])
                length = len(list_class)
                if len(oneline) > 5:
                    index = str(index) + ":" + oneline[5]
                    namecontent = xmlBuilder.createTextNode(str(index))
                else:
                    namecontent = xmlBuilder.createTextNode(str(index) if
                                                            length < 1 else list_class[index]
                                                            if index < length else str(index))
                picname.appendChild(namecontent)
                object.appendChild(picname)  # name标签结束
                pose = xmlBuilder.createElement("pose")  # pose标签
                posecontent = xmlBuilder.createTextNode("Unspecified")
                pose.appendChild(posecontent)
                object.appendChild(pose)  # pose标签结束

                truncated = xmlBuilder.createElement("truncated")  # truncated标签
                truncatedContent = xmlBuilder.createTextNode("0")
                truncated.appendChild(truncatedContent)
                object.appendChild(truncated)  # truncated标签结束

                difficult = xmlBuilder.createElement("difficult")  # difficult标签
                difficultcontent = xmlBuilder.createTextNode("0")
                difficult.appendChild(difficultcontent)
                object.appendChild(difficult)  # difficult标签结束

                bndbox = xmlBuilder.createElement("bndbox")  # bndbox标签
                xmin = xmlBuilder.createElement("xmin")  # xmin标签
                mathData = int(((float(oneline[1])) * Pwidth + 1) - (float(oneline[3])) * 0.5 * Pwidth)
                xminContent = xmlBuilder.createTextNode(str(mathData))
                xmin.appendChild(xminContent)
                bndbox.appendChild(xmin)  # xmin标签结束

                ymin = xmlBuilder.createElement("ymin")  # ymin标签
                mathData = int(((float(oneline[2])) * Pheight + 1) - (float(oneline[4])) * 0.5 * Pheight)
                yminContent = xmlBuilder.createTextNode(str(mathData))
                ymin.appendChild(yminContent)
                bndbox.appendChild(ymin)  # ymin标签结束

                xmax = xmlBuilder.createElement("xmax")  # xmax标签
                mathData = int(((float(oneline[1])) * Pwidth + 1) + (float(oneline[3])) * 0.5 * Pwidth)
                xmaxContent = xmlBuilder.createTextNode(str(mathData))
                xmax.appendChild(xmaxContent)
                bndbox.appendChild(xmax)  # xmax标签结束

                ymax = xmlBuilder.createElement("ymax")  # ymax标签
                mathData = int(((float(oneline[2])) * Pheight + 1) + (float(oneline[4])) * 0.5 * Pheight)
                ymaxContent = xmlBuilder.createTextNode(str(mathData))
                ymax.appendChild(ymaxContent)
                bndbox.appendChild(ymax)  # ymax标签结束
                object.appendChild(bndbox)  # bndbox标签结束
                annotation.appendChild(object)  # object标签结束

            dst_path = name.split(f'{os.sep}')[-1][0:-4]
            f = open(xmlPath + os.sep + dst_path + ".xml", 'w')
            xmlBuilder.writexml(f, indent='\t', newl='\n', addindent='\t', encoding='utf-8')
            f.close()
        mainqueue.put({'stop': True})

    def json2txt(self,json_path: str, label_path: str, names):
        '''
        {
            "shapes": [
                {
                    "label": "",
                    "points": [
                        [1, 1],
                    ],
                    "shape_type": "polygon",
                }
            ],
            "imagePath": "1.jpg",
            "imageHeight": 1,
            "imageWidth": 1
        }
        '''
        import json
        label = json.load(open(json_path))

        try:
            width = label['imageWidth']
            height = label['imageHeight']
        except:
            print('Could not parse size in ', json_path)
            return

        txt_content = ''
        names = set(names)
        for obj in label['shapes']:
            names.add(obj['label'])
            id_ = list(names).index(obj['label'])
            txt_content += str(id_)
            points = obj['points']

            for point in points:
                if not point: continue
                txt_content += ' ' + str(float(point[0]) / width)
                txt_content += ' ' + str(float(point[1]) / height)

            txt_content += '\n'

        open(label_path, 'w').write(txt_content)

        return names

    def convert(self,size, box):
        '''
        input:
        size:(width,height);
        box:(x1,x2,y1,y2)
        output:
        (x,y,w,h)
        '''
        dw = 1. / size[0]
        dh = 1. / size[1]
        x = (box[0] + box[1]) / 2.0
        y = (box[2] + box[3]) / 2.0
        w = box[1] - box[0]
        h = box[3] - box[2]
        x = x * dw
        w = w * dw
        y = y * dh
        h = h * dh
        return (x, y, w, h)

    def json2txt_(self,path_json, path_txt,names):
        with open(path_json, "r") as path_json:
            jsonx = json.load(path_json)
            try:
                width = int(jsonx["imageWidth"])  # 原图的宽
                height = int(jsonx["imageHeight"])  # 原图的高
            except Exception as e:
                return names
            try:
                names = collections.OrderedDict(names)
                with open(path_txt, "w+") as ftxt:
                    # 遍历每一个bbox对象
                    for shape in jsonx["shapes"]:
                        obj_cls = str(shape["label"])  # 获取类别
                        # list_class = names.split(",")
                        # cls_id = list_class.index(obj_cls)  # 获取类别索引
                        names[obj_cls] = None
                        cls_id = list(names).index(obj_cls)
                        points = np.array(shape["points"])  # 获取(x1,y1,x2,y2)
                        x1 = int(points[0][0])
                        y1 = int(points[0][1])
                        x2 = int(points[1][0])
                        y2 = int(points[1][1])
                        # (左上角,右下角) -> (中心点,宽高) 归一化
                        bb = self.convert((width, height), (x1, x2, y1, y2))
                        ftxt.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + "\n")
                return names
            except Exception as e:
                return names
                print(str(e))

    def json2txt_pre(self,dir_json: str, dir_txt: str, names,mainqueue):
        if not os.path.exists(dir_txt):
            os.makedirs(dir_txt)
        list_json = get_img_list(dir_json,[], ext=['json', 'JSON'])
        for cnt, json_path in enumerate(list_json):
            print("cnt=%d,name=%s" % (cnt, json_path))
            try:
                if mainqueue.get(block=False):
                    mainqueue.put({'stop': True})
                    break
            except Exception as e:
                pass
            json_name = os.path.basename(json_path)
            path_txt = dir_txt + os.sep + json_name.replace(".json", ".txt")
            names = self.json2txt_(json_path, path_txt,names)
        mainqueue.put({'stop': True})

if __name__ == '__main__':

    def read_image_label(path_to_img: str, path_to_txt: str, normilize: bool = False):

        # read image
        image = cv2.imread(path_to_img)
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        img_h, img_w = image.shape[:2]

        # read .txt file for this image
        with open(path_to_txt, "r") as f:
            txt_file = f.readlines()[0].split()
            cls_idx = txt_file[0]
            coords = txt_file[1:]
            polygon = np.array([[eval(x), eval(y)] for x, y in
                                zip(coords[0::2], coords[1::2])])  # convert list of coordinates to numpy massive

        # Convert normilized coordinates of polygons to coordinates of image
        if normilize:
            polygon[:, 0] = polygon[:, 0] * img_w
            polygon[:, 1] = polygon[:, 1] * img_h
        return image, polygon.astype(np.int)

    def show_image_mask(img: np.array, polygon: np.array, alpha: float = 0.7):

        import matplotlib.pyplot as plt
        # Create zero array for mask
        mask = np.zeros((img.shape[0], img.shape[1]), dtype=np.uint8)
        overlay = img.copy()

        # Draw polygon on the image and mask
        cv2.fillPoly(mask, pts=[polygon], color=(255, 255, 255))
        cv2.fillPoly(img, pts=[polygon], color=(255, 0, 0))
        cv2.addWeighted(overlay, alpha, image, 1 - alpha, 0, image)

        # Plot image with mask
        fig = plt.figure(figsize=(22, 18))
        axes = fig.subplots(nrows=1, ncols=2)
        axes[0].imshow(img)
        axes[1].imshow(mask, cmap="Greys_r")
        axes[0].set_title("Original image with mask")
        axes[1].set_title("Mask")
        # plt.show()
        return mask


    path_to_img = r"C:\Users\SV00290116\Downloads\coco128-seg\images\train2017\000000000025.jpg"
    path_to_txt = r"C:\Users\SV00290116\Downloads\coco128-seg\labels\train2017\000000000025.txt"
    image, polygon = read_image_label(path_to_img, path_to_txt, True)
    mask = show_image_mask(image,polygon)

    # 您可能没有多边形数据，但有二进制掩码。因此拥有将二进制掩码转换为多边形的功能将很有用。此类功能的示例如下所示
    def mask_to_polygon(mask: np.array, report: bool = False):
        contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
        polygons = []
        for object in contours:
            coords = []

            for point in object:
                coords.append(int(point[0][0]))
                coords.append(int(point[0][1]))
            polygons.append(coords)

        if report:
            print(f"Number of points = {len(polygons[0])}")
        return np.array(polygons).ravel().tolist()

    def reduce_polygon(polygon: np.array, angle_th: int = 0, distance_th: int = 0):
        angle_th_rad = np.deg2rad(angle_th)
        points_removed = [0]
        while len(points_removed):
            points_removed = list()
            for i in range(0, len(polygon) - 2, 2):
                v01 = polygon[i - 1] - polygon[i]
                v12 = polygon[i] - polygon[i + 1]
                d01 = np.linalg.norm(v01)
                d12 = np.linalg.norm(v12)
                if d01 < distance_th and d12 < distance_th:
                    points_removed.append(i)
                    continue
                    angle = np.arccos(np.sum(v01 * v12) / (d01 * d12))
                    if angle < angle_th_rad:
                        points_removed.append(i)
            polygon = np.delete(polygon, points_removed, axis=0)
        return polygon

    def show_result_reducing(polygon) :
        import matplotlib.pyplot as plt
        original_polygon = np.array([[x, y] for x, y in zip(polygon[0::2], polygon[1::2])])

        reduced_polygon = reduce_polygon(original_polygon, angle_th=1, distance_th=20)

        fig = plt.figure(figsize=(16, 5))
        axes = fig.subplots(nrows=1, ncols=2)
        axes[0].scatter(original_polygon[:, 0], original_polygon[:, 1], label=f"{len(original_polygon)}", c='b',
                        marker='x', s=2)
        axes[1].scatter(reduced_polygon[:, 0], reduced_polygon[:, 1], label=f"{len(reduced_polygon)}", c='b',
                        marker='x', s=2)
        axes[0].invert_yaxis()
        axes[1].invert_yaxis()

        axes[0].set_title("Original polygon")
        axes[1].set_title("Reduced polygon")
        axes[0].legend()
        axes[1].legend()

        plt.show()

        print("\n\n", f'[bold black] Original_polygon length[/bold black]: {len(original_polygon)}\n',
              f'[bold black] Reduced_polygon length[/bold black]: {len(reduced_polygon)}\n'
              f'[bold black]Running time[/bold black]: {round(0 - 0, 4)} seconds')

        return reduced_polygon

    polygons = mask_to_polygon(mask, report=True)
    show_result_reducing(polygons)
